package com.fluidops.keyword;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.turtle.TurtleWriter;
import org.openrdf.sail.Sail;

import com.fluidops.keyword.label.provider.EurostatLabelProvider;
import com.fluidops.keyword.label.provider.WorldbankLabelProvider;
import com.fluidops.keyword.label.provider.Vocabulary.DCTERMS;
import com.fluidops.keyword.label.provider.Vocabulary.SKOS;

public class RepoCreate {

	public static final String catalogSubject = "http://keywordsearch.index/";
	public static final String catalogProperty = "http://keywordsearch.has.catalog/";

	private static final  String luceneNamespace = "http://www.ontotext.com/owlim/lucene#";

	public static final String EXCLUDE_ENTITIES_PARAM = luceneNamespace + "excludeEntities";
	public static final String EXCLUDE_PREDICATES_PARAM = luceneNamespace + "excludePredicates";
	
	public static final String INCLUDE_PREDICATES_PARAM = luceneNamespace + "includePredicates";

	public static final String INCLUDE_PARAM = luceneNamespace + "include";
	public static final String INDEX_PARAM = luceneNamespace + "index";
	public static final String LANGUAGE_PARAM = luceneNamespace + "languages";

	public static final String MOLECULE_SIZE_PARAM = luceneNamespace + "moleculeSize"; 

	public static final String SET_LUCENE_PARAM = luceneNamespace + "setParam";

	public static final String INDEX_NAME = "myLuceneIndex";
	
	public static final String LUCENE_INDEX_NAME_PARAM = luceneNamespace + INDEX_NAME;
	public static final String CREATE_LUCENE_INDEX_PARAM = luceneNamespace + "createIndex";

	public static final String USE_RDF_RANK_PARAM = luceneNamespace + "useRDFRank";
	
	public static final String REPO_LOCATION_PROPERTY = "repoLocation";

	public static final String ES_INPUT_PROPERTY = "esInput";
	public static final String WB_INPUT_PROPERTY = "wbInput";

	public static final String ES_OUTPUT_PROPERTY = "esOutput";
	public static final String WB_OUTPUT_PROPERTY = "wbOutput";
	public static final String LOGGER_PROPERTY = "loggerProperties";
	

	private static ValueFactory vf = ValueFactoryImpl.getInstance(); 

	private static Logger logger = Logger.getLogger(RepoCreate.class.getName());

	public static void main(String[] args) throws Exception{

		if(args.length > 0){
			Properties prop = new Properties();
			prop.load(new FileReader(args[0]));

			PropertyConfigurator.configure(prop.getProperty(LOGGER_PROPERTY));


			String location = prop.getProperty(REPO_LOCATION_PROPERTY);
			logger.info("Set repository location to " + location);
			Repository repo = initRepo(location);

			boolean createIndex = false;
			if(!hasIndexOfCatalog(repo, EurostatLabelProvider.eurostatRDFNamespace)){

				if(!prop.containsKey(ES_INPUT_PROPERTY) || !(new File(prop.getProperty(ES_INPUT_PROPERTY))).exists()){
					//load es labels from the web
					logger.info("Loading the Eurostat labels from the web.");
					EurostatLabelProvider esLabelProvider = new EurostatLabelProvider();
					List<Statement> esLabels = esLabelProvider.getLabelStatements();
					String esOut = prop.getProperty(ES_OUTPUT_PROPERTY);
					logger.info("Write es_labels to file " + esOut);
					storeToFile(esOut, esLabels);
					storeToRepo(repo, esLabels);
					logger.info("Finished loading es_labels to file.");
				}
				else{
					// we have already an file load it from there
					File esInput = new File(prop.getProperty(ES_INPUT_PROPERTY));
					logger.info("Starting to load input file of eurostat labels " + esInput.getAbsolutePath());
					RepositoryConnection con = repo.getConnection();
					con.add(esInput, "", RDFFormat.TURTLE);
					con.commit();
					con.close();
					logger.info("Eurostat labels loaded. ");
				}
				createIndex = true;
			}else{
				logger.info("Labels of Eurostat already stored.");
			}

			if(!hasIndexOfCatalog(repo, WorldbankLabelProvider.CATALOG_NAMESPACE)){
				if(!prop.containsKey(WB_INPUT_PROPERTY) || !(new File(prop.getProperty(WB_INPUT_PROPERTY))).exists()){
					//load worldbank labels from the web
					logger.info("Loading the WorldBank labels from the web.");
					WorldbankLabelProvider wbLabelProvider = new WorldbankLabelProvider();
					List<Statement> wbLabels = wbLabelProvider.getLabelStatements();
					String wbOut = prop.getProperty(WB_OUTPUT_PROPERTY);
					logger.info("Write es_labels to file " + wbOut);
					storeToFile(wbOut, wbLabels);
					storeToRepo(repo, wbLabels);
					logger.info("Finished loading worldbank labels to file.");
				}else{
					// we have already an file load it from there
					File wbInput = new File(prop.getProperty(WB_INPUT_PROPERTY));
					logger.info("Starting to load input file of worldbank labels " + wbInput.getAbsolutePath());
					RepositoryConnection con = repo.getConnection();
					con.add(wbInput, "", RDFFormat.TURTLE);
					con.commit();
					con.close();
					logger.info("Worldbank labels loaded.");
					createIndex = true;
				}
				createIndex = true;
			}else{
				logger.info("Labels of WorldBank already stored.");
			}
			
			if(createIndex){
				// remove has catalog URIs from the search
				ask(repo, vf.createURI(EXCLUDE_ENTITIES_PARAM), vf.createURI(SET_LUCENE_PARAM), vf.createURI(catalogSubject));
				ask(repo, vf.createURI(EXCLUDE_PREDICATES_PARAM), vf.createURI(SET_LUCENE_PARAM), vf.createURI(catalogProperty));
			
				//specify the molecules
				ask(repo, vf.createURI(INCLUDE_PARAM), vf.createURI(SET_LUCENE_PARAM), vf.createLiteral("literals"));

				// set the data types to be indexed
				ask(repo, vf.createURI(INDEX_PARAM), vf.createURI(SET_LUCENE_PARAM), vf.createLiteral("literals"));
				ask(repo, vf.createURI(LANGUAGE_PARAM), vf.createURI(SET_LUCENE_PARAM), vf.createLiteral("en,none"));
				
				//set the properties to be included in the traversal
				ask(repo, vf.createURI(INCLUDE_PREDICATES_PARAM) , vf.createURI(SET_LUCENE_PARAM), DCTERMS.TITLE);
				ask(repo, vf.createURI(INCLUDE_PREDICATES_PARAM) , vf.createURI(SET_LUCENE_PARAM), DCTERMS.DESCRIPTION);
				ask(repo, vf.createURI(INCLUDE_PREDICATES_PARAM) , vf.createURI(SET_LUCENE_PARAM), SKOS.PREFLABEL);

				//set the length of the path a molecule is reached 
				ask(repo, vf.createURI(MOLECULE_SIZE_PARAM), vf.createURI(SET_LUCENE_PARAM), vf.createLiteral(2));
				
				//create the index
				logger.info("Start to create the lucene index.");
				ask(repo, vf.createURI(LUCENE_INDEX_NAME_PARAM), vf.createURI(CREATE_LUCENE_INDEX_PARAM), vf.createLiteral(true));
				logger.info("Lucene index created.");
			}
			
			repo.shutDown();
		} else{
			System.out.println("Please set the config properties file");
		}
	}



	public static Repository initRepo(String location) throws Exception{
		Repository repo;

		System.setProperty("ruleset", "empty"); // for performance reasons
		System.setProperty("repository-type", "weighted-file-repository");
		System.setProperty("console-thread", "false");
		System.setProperty("storage-folder", location);
		System.setProperty("build-pcsot", "true");
		repo = new SailRepository((Sail) 
				Class.forName("com.ontotext.trree.OwlimSchemaRepository")
				.getConstructor().newInstance());
		repo.initialize();
		logger.info("Repository initialized at " + location);

		return repo;
	}

	public static boolean hasIndexOfCatalog(Repository repo, String catalog) throws Exception{
		return ask(repo, vf.createURI(catalogSubject), vf.createURI(catalogProperty), vf.createURI(catalog));
	}


	public static boolean ask(Repository repo, Resource sub, URI pred, Value obj)throws Exception{
		return ask(repo, vf.createStatement(sub, pred, obj));
	}

	public static boolean ask(Repository repo, Statement s) throws Exception{
		RepositoryConnection con = repo.getConnection();
		boolean indexed = con.hasStatement(s, false);
		con.close();
		
		return indexed;
	}

	public static void storeToRepo(Repository repo, List<Statement> statements) throws Exception{
		RepositoryConnection con = repo.getConnection();
		con.setAutoCommit(false);
		for(Statement s : statements){
			con.add(s);
		}
		con.commit();
		con.close();
	}
	
	public static void storeToFile(String location, List<Statement> statements) throws Exception{
		File output = new File(location);
		output.createNewFile();

		TurtleWriter tw = new TurtleWriter(new BufferedWriter(new FileWriter(output), 1024 * 4));
		tw.startRDF();
		for(Statement s : statements){
			tw.handleStatement(s);
		}

		tw.endRDF();
	}

}
